/**
 *Written By : A. Kenan Kule	
 *Zehra Cataltepe: May 21, 2011: Corrected GetCorrelation Method
 */
package Analysis;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

public class PengMIClass extends CorrelationAbstractClass {
	
	

	//Return the correlation between inputs in xMatrix and outputs in yMatrix
	//Outputs are encoded as 1, 2, 3, ... as opposed to 1000 0100 0010 0001
	public synchronized static double[] GetCorrelation (double xMatrix[][], double labels[]) 
	{
		int numberOfFeatures = xMatrix[0].length;
		int numberOfInstances = xMatrix.length;
		double correlation[] = new double[numberOfFeatures];
		double[] x = new double[numberOfInstances];

		for(int i=0 ; i<numberOfFeatures; i++)
		{
			correlation[i] = 0 ; 
			for(int j=0; j<numberOfInstances ;j++)
			{
				x[j]= xMatrix[j][i];
			}
			try {
				correlation[i] += getMutualInfo(x, labels);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return correlation;
	}
	
	//Return the average redundancies between a set of features and another set of selected features
	public static double[] GetRedundancy (double xMatrix[][], int indxFeaturesEvaluate[], int indxFeaturesSelected[]) 
	{
		int numberOfInstances = xMatrix.length;
		double [][] xMatrixSelected = new double[numberOfInstances][indxFeaturesSelected.length] ; 
		for (int i=0 ; i<numberOfInstances; i++)
			for(int j=0 ; j<indxFeaturesSelected.length; j++)
				xMatrixSelected[i][j] = xMatrix[i][indxFeaturesSelected[j]] ; 
		
		
		double redundancies[] = new double[indxFeaturesEvaluate.length];
		double x[] = new double[numberOfInstances] ; 
		double individualFeatureRedundancies[] = new double[indxFeaturesSelected.length] ;
		for(int i=0 ; i<indxFeaturesEvaluate.length; i++)
		{
			for(int j=0; j<numberOfInstances ;j++)
			{
				x[j]= xMatrix[j][indxFeaturesEvaluate[i]];
			}
			try 
			{
				individualFeatureRedundancies = GetCorrelation(xMatrixSelected, x);
				redundancies[i] = 0 ; 
				for (int k=0; k<individualFeatureRedundancies.length ;k++)
				{
					redundancies[i] += individualFeatureRedundancies[k];
				}
				redundancies[i] = redundancies[i]/individualFeatureRedundancies.length ; 
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return redundancies;
	}
	
	public static double getMutualInfo(double[] vec1,double[] vec2) {
		
		List<Object> args = estpab(vec1, vec2);
		return estmutualinfo((double[][])args.get(0), (double[])args.get(1), (double[])args.get(2));
	}

	private static double estmutualinfo(double [][] p12, double [] p1, double [] p2) {
		double mi = 0;
		for (int i = 0; i < p1.length; i++) {
			for (int j = 0; j < p2.length; j++) {
				if(p12[i][j] != 0)
					mi += p12[i][j] * Math.log(p12[i][j]/(p1[i]*p2[j]));
			}
		}
		return mi;
	}


	private static List<Object> estpab(double[] vec1, double[] vec2) {

		
		int i, j;
		if(vec1.length != vec2.length)
			throw new IndexOutOfBoundsException("Vector sizes are not equal");
		int [] vec1disc = copyvecdata(vec1);
		int [] vec2disc = copyvecdata(vec2);
		int nstate1 = vec1disc[0];for(int ii: vec1disc)if (nstate1 < ii) nstate1 = ii;nstate1++;
		int nstate2 = vec2disc[0];for(int ii : vec2disc)if (nstate2 < ii) nstate2 = ii;nstate2++;
		
		double [][] p12 = new double[nstate1][nstate2];
		double [] p1 = new double[nstate1];
		double [] p2 = new double[nstate2];
		
		for(i = 0;i < nstate1; i++){
			for(j = 0;j < nstate2; j++){
				p12[i][j] = 0;
			}
			p1[i] = 0;
		}
		
		for(j = 0;j < nstate2; j++)
			p2[j] = 0;
		
		for(i = 0;i < vec1disc.length; i++){
				p12[vec1disc[i]][vec2disc[i]]++; 
		}
		

		for(i = 0;i < nstate1;i++)
			for(j = 0;j < nstate2;j++){
				
				p12[i][j] /= vec1disc.length;
				p1[i] += p12[i][j];
				p2[j] += p12[i][j];
			}
		
		List<Object> args = new ArrayList<Object>();
		args.add(p12);
		args.add(p1);
		args.add(p2);
		return args;
	}
	
	private static int[] copyvecdata(double[] vec1){
		
		  int i;
		  int minn,maxx;
		  int [] desdata = new int[vec1.length];
		  if (vec1[0]>0)
		    maxx = minn = (int)(vec1[0]+0.5);
		  else
		    maxx = minn = (int)(vec1[0]-0.5);

		  int tmp;
		  double tmp1;
		  for (i=0;i<vec1.length;i++)
		  {
		    tmp1 = vec1[i];
		    tmp = (tmp1>0)?(int)(tmp1+0.5):(int)(tmp1-0.5);//round to integers
		    minn = (minn<tmp)?minn:tmp;
		    maxx = (maxx>tmp)?maxx:tmp;
		    desdata[i] = tmp;
		  }

		  //make the vector data begin from 0 (i.e. 1st state)
		  for (i=0;i<vec1.length;i++)
		  {
		    desdata[i] -= minn;
		  }

		  return desdata;
	}


	public void Test() {
		
		Random random = new Random();
		random.setSeed(0);
		
		System.out.println("Peng MI Test: ");
		
		int numSamples = 5;
		double [] vec1 = new double[numSamples];
		double [] vec2 = new double[numSamples];
		
		for (int i = 0; i < numSamples; i++) 
		{
			vec1[i] = random.nextDouble() ;
			System.out.print(vec1[i] +"  ");		
		}
		System.out.println();
		for (int i = 0; i < numSamples; i++) 
		{			
			vec2[i] = random.nextDouble() ;
			System.out.print(vec2[i] + "  ");
		}
		System.out.println();
		System.out.println("After Sorting vector 2: ");
		Arrays.sort(vec2);
		for (double d : vec2) {
			System.out.print(d+ "  ");
		}
		System.out.println();
		
//double []vec1 = {0, 0.23, -1.39, -0.91, -1.57, 0.86, 0.54, -1.16, 0.59, 0.35, -0.22, -1.91, -1.20, -0.79, -0.96, 0.07, 0.91, -1.24, -1.66, 0.65, -1.25};
//double []vec2 = {0.86, -1.67, -1.61, -1.15, -0.53, -0.98, -1.63, -1.22, 0, -1.20, -1.83, -1.13, -0.95, -0.45, 0.69, -0.09, -1.43, -0.97, -1.97, 0.97, -0.31};
		try {
			System.out.println("MI:   "+getMutualInfo(vec1, vec2));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	

}

